package myapp.framelibrary.db;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v4.util.ArrayMap;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Created by 超 on 2017/6/27.
 */

public class DaoSupport<T> implements IDaoSupport<T> {
    private SQLiteDatabase mSqLiteDatabase;
    private Class<T> mClazz;
    private static final Object[] putMethodArgs = new Object[2];
    private static final Map<String, Method> mPutMethod = new ArrayMap<>();

    @Override
    public void init(SQLiteDatabase sqLiteDatabase, Class<T> clazz) {
        this.mClazz = clazz;
        this.mSqLiteDatabase = sqLiteDatabase;
        StringBuffer sb = new StringBuffer();
        Field[] declaredFields = clazz.getClass().getDeclaredFields();
        sb.append("create table if not exists ").append(clazz.getSimpleName()).append("(id integer primary key autoincrenment ,");
        for (Field field : declaredFields) {
            String name = field.getName();
            String type = field.getType().getSimpleName();
            sb.append(name).append(DaoUtil.getColumnType(type)).append(", ");
        }
        sb.replace(sb.length() - 2, sb.length(), ")");
        String createSql = sb.toString();
        sqLiteDatabase.execSQL(createSql);
    }

    @Override
    public void inerts(List<T> list) {
        mSqLiteDatabase.beginTransaction();
        for (T t : list) {
            inert(t);
        }
        mSqLiteDatabase.setTransactionSuccessful();
        mSqLiteDatabase.endTransaction();
    }

    @Override
    public List<T> query() {
        Cursor query = mSqLiteDatabase.query(mClazz.getSimpleName(),
                null, null, null,
                null, null, null);
        List<T> list = cursorToList(query);
        return list;
    }

    private List<T> cursorToList(Cursor query) {
        List<T> list = new ArrayList<>();
        if (query != null && query.moveToFirst()) {
            do {
                try {
                    T instance = mClazz.newInstance();
                    Field[] declaredFields = mClazz.getDeclaredFields();
                    for (Field declaredField : declaredFields) {
                        declaredField.setAccessible(true);
                        String key = declaredField.getName();
                        int index = query.getColumnIndex(key);
                        if (index == -1) {
                            continue;
                        }
                        Method cursorMethod = cursorMethod(declaredField.getType());
                        if (cursorMethod != null) {
                            Object value = cursorMethod.invoke(query, index);
                            if (value == null) {
                                continue;
                            }
                            if (declaredField.getType() == boolean.class || declaredField.getType() == Boolean.class) {
                                if ("0".equals(String.valueOf(value))) {
                                    value = false;
                                } else if ("1".equals(String.valueOf(value))) {
                                    value = true;
                                }
                            } else if (declaredField.getType() == char.class || declaredField.getType() == Character.class) {
                                value = ((String) value).charAt(0);
                            } else if (declaredField.getType() == Date.class) {
                                long date = (long) value;
                                if (date < 0) {
                                    value = null;
                                } else {
                                    value = new Date(date);
                                }
                            }
                            declaredField.set(instance, value);
                        }
                        list.add(instance);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } while (query.moveToNext());
        }
        return list;
    }

    private Method cursorMethod(Class<?> type) throws Exception {
        String methodName = getColumnMethodName(type);
        Method method = Cursor.class.getMethod(methodName, int.class);
        return method;
    }

    private String getColumnMethodName(Class<?> type) {
        String typeName;
        if (type.isPrimitive()) {
            typeName = DaoUtil.capitalize(type.getName());
        } else {
            typeName = type.getSimpleName();
        }
        String methodName = "get" + typeName;
        if ("getBoolean".equals(methodName)) {
            methodName = "getInt";

        } else if ("getChar".equals(methodName) || "getCharacher".equals(methodName)) {
            methodName = "getString";
        } else if ("getDate".equals(methodName)) {
            methodName = "getLong";
        } else if ("getInteger".equals(methodName)) {
            methodName = "getInt";
        }
        return methodName;

    }

    @Override
    public Long inert(T t) {

        ContentValues contentValues = contentValueByObj(t);
        return mSqLiteDatabase.insert(mClazz.getSimpleName(), null, contentValues);
    }

    private ContentValues contentValueByObj(T t) {
        ContentValues values = new ContentValues();
        Field[] fields = mClazz.getDeclaredFields();
        for (Field field : fields) {

            try {
                field.setAccessible(true);
                Object value = field.get(t);
                String key = field.getName();
                //参照Google 源码 new出TextView 等view进行缓存
                putMethodArgs[0] = key;
                putMethodArgs[1] = value;
                Method putMethod = mPutMethod.get(key);
                if (putMethod == null) {
                    putMethod = ContentValues.class.getDeclaredMethod("put", String.class, values.getClass());
                    mPutMethod.put(key, putMethod);
                }

                putMethod.invoke(values, putMethodArgs);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                putMethodArgs[0] = null;
                putMethodArgs[1] = null;
            }
        }

        return values;
    }
}
